﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ButsaForecast.Dialogs;
using ButsaForecast.SpecialTypes;
using ButsaForecast.al;
using ButsaLib;
using ButsaLib.Models;
using System.Linq;
using ButsaScraperLib;
using CustomConfigurationSettings;
using Ninject;
using ForecastPair = ButsaLib.Models.ForecastPair;

namespace ButsaForecast
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow
	{
		public MainWindow()
		{
			InitializeComponent();

			ButsaDbContext butsaDbContext = new ButsaDbContext();
			butsaDbContext.ForecastSnapshots.OrderBy(item => item.SnapshotName).Load();
			SnapshotsList.DataContext = butsaDbContext.ForecastSnapshots.Local;
			SnapshotsList.SelectionChanged += SnapshotsListSelectionChanged;
		}

		public static ButsaDbContext GetDbContext()
		{
			var connectionString = CustomConfigurationManager.Instance.GetConfigurationReader
				.GetConfigurationValue("ButsaDbConnectionString");
			return new ButsaDbContext(connectionString);
		}

		public static string GetConnectionString()
		{
			return CustomConfigurationManager.Instance.GetConfigurationReader
				.GetConfigurationValue("ButsaDbConnectionString");
		}

		private void SnapshotsListSelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			var fs = ((ForecastSnapshot) SnapshotsList.SelectedItem);

			ButsaDbContext butsaDbContext = new ButsaDbContext();

			butsaDbContext.ForecastPairs
				.Where(item => item.ForecastSnapshot.ForecastSnapshotId == fs.ForecastSnapshotId)
				.Include("HomeTeamShot")
				.Include("GuestTeamShot")
				.Load();

			ForecastPairsList.DataContext = butsaDbContext.ForecastPairs.Local;
		}

		private void ForecastPairsListMouseDoubleClick(object sender, MouseButtonEventArgs e)
		{
			if (ForecastPairsList.SelectedItem != null)
			{
				ForecastPair forecastPair = (ForecastPair) ForecastPairsList.SelectedItem;
				TeamCompareDialog teamCompareDialog = new TeamCompareDialog(forecastPair);
				teamCompareDialog.ShowDialog();
			}
		}

		/// <summary>
		/// Генерация статистики.
		/// </summary>
		private void GenerateStatisticsClick(object sender, RoutedEventArgs e)
		{
			IKernel kernel = new StandardKernel();
			kernel.Bind<IDxPairCalculation>().To<DxPairCalculationB>();
			DxAlgorithm dxAlgorithm = new DxAlgorithm(kernel, 12, GetConnectionString(), 0.05);

			dxAlgorithm.AnalyzisCompleted += (a, b) => Dispatcher.BeginInvoke(
				new Action(() =>
				           	{
				           		var message = new StringBuilder();
				           		//подобранные интервалы
				           		foreach (
				           			var intervalStat in
				           				dxAlgorithm.IntervalStats)
				           		{
				           			message.AppendFormat(
				           				"A: {0}({1})-{2}({3}):{4}-{5}:\t{6}\t{7}\r\n",
				           				intervalStat.Start.ToString("0.00"),
				           				intervalStat.StartPosition,
				           				intervalStat.End.ToString("0.00"),
				           				intervalStat.EndPosition,
				           				intervalStat.Hg,
				           				intervalStat.Gg,
				           				intervalStat.Balls,
				           				(double) intervalStat.Balls/
				           				((intervalStat.EndPosition - intervalStat.StartPosition)*3.0));
				           		}
				           		MessageDialog messageDialog =
				           			new MessageDialog(message.ToString()) {Owner = this};
				           		messageDialog.ShowDialog();
				           	}));

			dxAlgorithm.StartAnalyzis();
		}

		private static int CalculateBall(ForecastPair forecastPair)
		{
			//int b = 0;
			//if (forecastPair.HomeTeamGoals == forecastPair.HomeTeamGoalsForecast &&
			//    forecastPair.GuestTeamGoals == forecastPair.GuestTeamGoalsForecast)
			//    b = 3;
			//else if (forecastPair.HomeTeamGoals - forecastPair.GuestTeamGoals ==
			//         forecastPair.HomeTeamGoalsForecast - forecastPair.GuestTeamGoalsForecast)
			//    b = 2;
			//else if (!(forecastPair.HomeTeamGoals > forecastPair.GuestTeamGoals) ^
			//         (forecastPair.HomeTeamGoalsForecast > forecastPair.GuestTeamGoalsForecast))
			//    b = 1;
			//return b;
			throw new NotImplementedException();
		}

		private void OnTarget(DxAlgorithm dxAlgorithm)
		{
			var message = new StringBuilder();

			foreach (ForecastSnapshot forecastSnapshot in SnapshotsList.Items)
			{
				var forecastedPairs = dxAlgorithm.MakeForecast(forecastSnapshot);
				int ball = forecastedPairs.Sum(forecastedPair => CalculateBall(forecastedPair));
				message.AppendLine(forecastSnapshot.SnapshotName + "\t" + ball.ToString(CultureInfo.InvariantCulture));
			}
			MessageDialog messageDialog = new MessageDialog(message.ToString()) {Owner = this};
			messageDialog.ShowDialog();
		}

		private void MakeForecastDx7ForAll(object sender, RoutedEventArgs e)
		{
			string forecast = Algorithm.Forecast();
			MessageDialog messageDialog = new MessageDialog(forecast);
			messageDialog.Owner = this;
			messageDialog.ShowDialog();
			////AlAlgorithm alAlgorithm = new AlAlgorithm();
			////var forecastSnapshot = ((ForecastSnapshot)SnapshotsList.SelectedItem);
			////if (!forecastSnapshot.ResultsApplied)
			////{
			////    List<MatchPair> matchPairs = new List<MatchPair>();
			////    throw new NotImplementedException();
			////    //read forecast table
			////    //create matchpairs
			////    var forecast = alAlgorithm.GetForecast(forecastSnapshot.ForecastSnapshotId, matchPairs);
			////}
			////IKernel kernel = new StandardKernel();
			////kernel.Bind<IDxPairCalculation>().To<DxPairCalculationB>();
			////DxAlgorithm dxAlgorithm = new DxAlgorithm(kernel, 12, GetConnectionString(), 0.05);
			////dxAlgorithm.AnalyzisCompleted += (a, b) => Dispatcher.BeginInvoke(new Action(() => OnTarget(dxAlgorithm)));
			////dxAlgorithm.StartAnalyzis();
		}

		private void BuildTeamsCacheButtonClick(object sender, RoutedEventArgs e)
		{
			BuildTeamsCacheButton.IsEnabled = false;
			Thread thread = new Thread(() => ButsaManager.Instance.ReadAllTeamsToCache(ReadAllTeamsToCacheCallback));
			thread.IsBackground = true;
			thread.Start();
		}

		private void ReadAllTeamsToCacheCallback(object sender, EventArgs args)
		{
			Dispatcher.BeginInvoke(
				new Action(() =>
				           	{
								if (sender != null)
								{
									BuildTeamsCacheButton.Content = String.Format("Кэш команд {0}", (int)sender);
								}
								else
								{
									//BuildTeamsCacheButton.Content = "Кэш команд";
									BuildTeamsCacheButton.IsEnabled = true;
								}
				           	}));
		}

		private void BuildDivisionsCacheButtonClick(object sender, RoutedEventArgs e)
		{
			BuildDivisionsCacheButton.IsEnabled = false;
			Thread thread = new Thread(() => ButsaManager.Instance.ReadAllDivisionsToCache(ReadAllDivisionsToCacheCallback));
			thread.IsBackground = false;
			thread.Start();
		}

		private void ReadAllDivisionsToCacheCallback(object sender, EventArgs args)
		{
			Dispatcher.BeginInvoke(new Action(() =>
			{
				if (sender != null)
				{
					BuildDivisionsCacheButton.Content = String.Format("Кэш дивизионов {0}", (int)sender);
				}
				else
				{
					BuildDivisionsCacheButton.Content = "Кэш дивизионов";
					BuildDivisionsCacheButton.IsEnabled = true;
				}
			}));
		}

		/// <summary>
		/// Загрузка всех команд.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void LoadAndSaveAllTeamsClick(object sender, RoutedEventArgs e)
		{
			LoadAndSaveAllTeams.IsEnabled = false;
			//запрашиваем новый снимок
			NewSnapshotDialog newSnapshotDialog = new NewSnapshotDialog();
			newSnapshotDialog.Owner = this;
			newSnapshotDialog.ShowDialog();
			var createdSnapshot = newSnapshotDialog.GetCreatedForecastSnapshot();

			Thread thread = new Thread(() =>
			                           	{
											//страны на закачку
			                           		List<string> countriesToDownload = new List<string>
			                           		                                   	{
			                           		                                   		"Англия",
			                           		                                   		"Болгария",
			                           		                                   		"Вануату",
			                           		                                   		"Германия",
			                           		                                   		"Голландия",
			                           		                                   		"Греция",
			                           		                                   		"Ирландия",
			                           		                                   		"Исландия",
			                           		                                   		"Испания",
			                           		                                   		"Куба",
			                           		                                   		"Мексика",
			                           		                                   		"Парагвай",
			                           		                                   		"Португалия",
			                           		                                   		"Северная Ирландия",
			                           		                                   		"Украина",
			                           		                                   		"Уэльс",
			                           		                                   		"Швейцария",
			                           		                                   		"Швеция",
			                           		                                   		"Шотландия",
			                           		                                   		"Эквадор",
			                           		                                   		"Япония"

			                           		                                   	};
											ButsaDbContext dbContext = new ButsaDbContext();
											dbContext.Configuration.AutoDetectChangesEnabled = false;
											List<string> teamsIds = dbContext.TeamsCache
												.Where(item => countriesToDownload.Contains(item.CountryName))
												.Select(item => item.TeamId)
												.ToList();
											var snapshot = dbContext.ForecastSnapshots
												.FirstOrDefault(item => item.ForecastSnapshotId == createdSnapshot.ForecastSnapshotId);
											if (snapshot != null)
											{
												var teamShots = ButsaManager.Instance.ReadTeamsShots(teamsIds, ReadTeamShotsCallback);
												snapshot.TeamShots = new List<TeamShot>();
												foreach (var teamShot in teamShots)
												{
													
													teamShot.ForecastSnapshot = snapshot;
													snapshot.TeamShots.Add(teamShot);
													foreach (var playerShot in teamShot.PlayerShots)
													{
														foreach (var bonusShot in playerShot.BonusShots)
														{
															dbContext.BonusShots.Add(bonusShot);
														}
														dbContext.PlayerShots.Add(playerShot);
													}
													dbContext.TeamShots.Add(teamShot);
												}
												dbContext.Entry(snapshot).State = EntityState.Modified;
												dbContext.SaveChanges();
											}
			                           		Dispatcher.BeginInvoke(new Action(() => { LoadAndSaveAllTeams.IsEnabled = true; }));
			                           	});
			LoadAndSaveAllTeams.Tag = 0;
			thread.Start();
		}

		private void ReadTeamShotsCallback(object sender, EventArgs args)
		{
			Dispatcher.BeginInvoke(new Action(() =>
			{
				if (sender != null)
				{
					LoadAndSaveAllTeams.Tag = (int)LoadAndSaveAllTeams.Tag + 1;
					LoadAndSaveAllTeams.Content = String.Format("Снимок всех команд {0}", (int)LoadAndSaveAllTeams.Tag);
				}
				else
				{
					LoadAndSaveAllTeams.Content = "Снимок всех команд";
				}
			}));
		}

		private void LoadResultsClick(object sender, RoutedEventArgs e)
		{
			if (SnapshotsList.SelectedItem != null)
			{
				var forecastSnapshot = ((ForecastSnapshot)SnapshotsList.SelectedItem);

				ButsaDbContext dbContext = new ButsaDbContext();

				var dbForecastSnapshot = dbContext.ForecastSnapshots
					.Include("TeamShots")
					.Include("ForecastPairs")
					.First(item => item.ForecastSnapshotId == forecastSnapshot.ForecastSnapshotId);

				//список идентификаторов дивизионов для чтения результатов с сайта
				List<int> divisionIds = dbForecastSnapshot.TeamShots
					.Select(item => item.DivisionName)
					.Distinct()
					.Join(dbContext.DivisionsCache, 
						outerItem => outerItem, 
						innerItem => innerItem.DivisionName,
						(outerItem, resultItem) => Int32.Parse(resultItem.DivisionId))
					.ToList();

				//запросить номер тура
				InputDialog inputDialog = new InputDialog("Номер тура") {Owner = this};
				var dialogResult = inputDialog.ShowDialog();
				if (!dialogResult.HasValue || !dialogResult.Value)
					return;

				//запрос номера тура
				int tourNumber = Int32.Parse(inputDialog.InputValue);

				Action a = () =>
				           	{
				           		//чтение результатов
				           		var results = ButsaManager.Instance.ReadResults(tourNumber, divisionIds);

				           		//кэш команд
				           		Dictionary<int, TeamShot> teamShotsHash = dbForecastSnapshot.TeamShots
				           			.ToDictionary(key => key.TeamId, value => value);

				           		if (dbForecastSnapshot.ForecastPairs != null && dbForecastSnapshot.ForecastPairs.Count >= 0)
				           		{
				           			dbForecastSnapshot.ForecastPairs.Clear();
				           			dbContext.SaveChanges();
				           		}
				           		dbForecastSnapshot.ForecastPairs = new List<ForecastPair>();
				           		foreach (var simpleResult in results)
				           		{
				           			ForecastPair forecastPair = new ForecastPair
				           			                            	{
				           			                            		ForecastSnapshot = dbForecastSnapshot,
				           			                            		HomeTeamShot = teamShotsHash[Int32.Parse(simpleResult.HomeTeamId)],
				           			                            		GuestTeamShot = teamShotsHash[Int32.Parse(simpleResult.GuestTeamId)],
				           			                            		HomeTeamGoals = simpleResult.HomeTeamGoals,
				           			                            		GuestTeamGoals = simpleResult.GuestTeamGoals
				           			                            	};
				           			forecastPair = dbContext.ForecastPairs.Add(forecastPair);
				           			dbForecastSnapshot.ForecastPairs.Add(forecastPair);
				           		}
				           		dbContext.SaveChanges();
				           		MessageBox.Show("Завершено!");
				           	};

				a.Invoke();

			}
		}

		private void MakeForecastDx7(object sender, RoutedEventArgs e)
		{
			TestTwo();
		}

		private static void TestOne()
		{
			ButsaDbContext dbContext = new ButsaDbContext();
			var forecastPairs = dbContext.ForecastPairs
				.Include(p => p.HomeTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.Include(p => p.GuestTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.ToList();

			var ivecs = forecastPairs.Select(item => new Ivec(item))
				.Select(item => new TeamPoint
				                	{
										HomeTeamEstimation = item.HomeTeamEstimation,
										GuestTeamEstimation =  item.GuestTeamEstimation,
				                		HG = item.HomeGoals,
				                		GDiff = item.HomeGoals - item.GuestGoals
				                	})
				.ToList();

			int boundary = (int) (ivecs.Count*0.95);
			var learnSet = ivecs.Take(boundary).ToList();
			var testSet = ivecs.Skip(boundary).Take(ivecs.Count - boundary).ToList();

			//рассчет набранных балов на тестовом множестве
			const int nearestSetCount = 40;
			const int corrleatedSetCount = 20;
			int averallBall = 0;
			foreach (var testVariable in testSet)
			{
				#region поиск [nearestSetCount] ближайших точек из learnSet

				TeamPoint[] nearestPoints = learnSet.Take(nearestSetCount).ToArray();
				for (int i = nearestSetCount; i < learnSet.Count; i++)
				{
					var learnPoint = learnSet[i];

					//среди 20 триплетов находим те, что от testVariable лежат дальше, чем learnPoint
					//и берем самый далекий
					TeamPoint replacedPoint = nearestPoints
						.Where(item => item.Distance(testVariable) - learnPoint.Distance(testVariable) > 0)
						.OrderByDescending(item => item.Distance(testVariable) - learnPoint.Distance(testVariable))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestPoints, replacedPoint);
						nearestPoints[replaceIndex] = learnPoint;
					}
				}

				#endregion

				#region поиск наиболее коррелирующих значений из набора ближайших [triplets]

				TeamPoint[] nearestCorrelatedPoints = nearestPoints.Take(corrleatedSetCount).ToArray();
				for (int i = corrleatedSetCount; i < nearestPoints.Length; i++)
				{
					var testPoint = nearestPoints[i];

					//среди [nearestSetCount] ближайших точек находим те, что хуже, чем testPoint коррелируют с testVariable
					TeamPoint replacedPoint = nearestCorrelatedPoints
						.Where(item => item.Correlation(testVariable) < testPoint.Correlation(testVariable))
						.OrderByDescending(item => item.Correlation(testVariable) - testPoint.Correlation(testVariable))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestCorrelatedPoints, replacedPoint);
						nearestCorrelatedPoints[replaceIndex] = testPoint;
					}
				}

				#endregion

				TeamPoint[] workingSet = nearestPoints.Where(item => item.Distance(testVariable) < 10).ToArray();
				if (workingSet.Length == 0)
					workingSet = nearestPoints;

				Debug.WriteLine(workingSet.Average(item => item.Distance(testVariable)));
				//Debug.WriteLine(workingSet[0].Correlation(testVariable));

				//вычисление наиболее частых разниц мячей среди ближайших [corrleatedSetCount] триплетов
				Dictionary<int, int> diffFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet)
				{
					if (diffFrequency.ContainsKey(triplet.GDiff))
						diffFrequency[triplet.GDiff] += 1;
					else
						diffFrequency[triplet.GDiff] = 1;
				}
				int mostFrequentGoalsDiff = diffFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

				//вычисление наиболее частого результата
				Dictionary<int, int> hgFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet.Where(item => item.GDiff == mostFrequentGoalsDiff))
				{
					if (hgFrequency.ContainsKey(triplet.HG))
						hgFrequency[triplet.HG] += 1;
					else
						hgFrequency[triplet.HG] = 1;
				}
				int mostFrequentHomeGoals = hgFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

				averallBall += CalculateBall(testVariable.HG, testVariable.HG - testVariable.GDiff,
				                             mostFrequentHomeGoals, mostFrequentHomeGoals - mostFrequentGoalsDiff);
			}

			//добиваемся 52%, сейчас - 35.8%
			MessageBox.Show(String.Format("Max result is {0}, taked {1} ({2}%).",
			                              testSet.Count*3,
			                              averallBall,
			                              ((averallBall/(testSet.Count*3.0))*100).ToString("0.00")));
		}

		private static void TestTwo()
		{
			ButsaDbContext dbContext = new ButsaDbContext();
			var forecastPairsLearnSet = dbContext.ForecastPairs
				.Where(item => !(item.ForecastSnapshot.ForecastSnapshotId == 21 && item.HomeTeamShot.CountryName.StartsWith("Швейцария")))
				.Include(p => p.HomeTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.Include(p => p.GuestTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.ToList();
			var forecastPairsTestSet = dbContext.ForecastPairs
				.Where(item => (item.ForecastSnapshot.ForecastSnapshotId == 21 && item.HomeTeamShot.CountryName.StartsWith("Швейцария")))
				.Include(p => p.HomeTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.Include(p => p.GuestTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.ToList();
			//анализируемое множество
			var learnSet = forecastPairsLearnSet.Select(item => new Ivec(item))
				.Select(item => new TeamPoint
				{
					HomeTeamEstimation = item.HomeTeamEstimation,
					GuestTeamEstimation = item.GuestTeamEstimation,
					HG = item.HomeGoals,
					GDiff = item.HomeGoals - item.GuestGoals
				})
				.ToList();
			//тестовое множество
			var testSet = forecastPairsTestSet.Select(item => new Ivec(item))
				.Select(item => new TeamPoint
				{
					HomeTeamEstimation = item.HomeTeamEstimation,
					GuestTeamEstimation = item.GuestTeamEstimation,
					HG = item.HomeGoals,
					GDiff = item.HomeGoals - item.GuestGoals
				})
				.ToList();
			foreach (var triplet in testSet)
			{
				Debug.WriteLine(triplet.HomeOtherGuests);
			}
			testSet = testSet
				.OrderByDescending(item => Math.Abs(item.HomeOtherGuests))
				.ToList()
				.Take(16)
				.ToList();
			foreach (var triplet in testSet)
			{
				Debug.WriteLine(triplet.HomeOtherGuests);
			}

			//рассчет набранных балов на тестовом множестве
			const int nearestSetCount = 40;
			const int corrleatedSetCount = 15;
			int averallBall = 0;
			foreach (var testVariable in testSet)
			{
				#region поиск [nearestSetCount] ближайших точек из learnSet

				TeamPoint[] nearestPoints = learnSet.Take(nearestSetCount).ToArray();
				for (int i = nearestSetCount; i < learnSet.Count; i++)
				{
					var learnPoint = learnSet[i];

					//среди 20 триплетов находим те, что от testVariable лежат дальше, чем learnPoint
					//и берем самый далекий
					TeamPoint replacedPoint = nearestPoints
						.Where(item => item.Distance(testVariable) - learnPoint.Distance(testVariable) > 0)
						.OrderByDescending(item => item.Distance(testVariable) - learnPoint.Distance(testVariable))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestPoints, replacedPoint);
						nearestPoints[replaceIndex] = learnPoint;
					}
				}

				#endregion

				#region поиск наиболее коррелирующих значений из набора ближайших [triplets]

				TeamPoint[] nearestCorrelatedPoints = nearestPoints.Take(corrleatedSetCount).ToArray();
				for (int i = corrleatedSetCount; i < nearestPoints.Length; i++)
				{
					var testPoint = nearestPoints[i];

					//среди [nearestSetCount] ближайших точек находим те, что хуже, чем testPoint коррелируют с testVariable
					TeamPoint replacedPoint = nearestCorrelatedPoints
						.Where(item => item.Correlation(testVariable) < testPoint.Correlation(testVariable))
						.OrderByDescending(item => item.Correlation(testVariable) - testPoint.Correlation(testVariable))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestCorrelatedPoints, replacedPoint);
						nearestCorrelatedPoints[replaceIndex] = testPoint;
					}
				}

				#endregion

				TeamPoint[] workingSet = nearestCorrelatedPoints.Where(item => item.Distance(testVariable) < 5).ToArray();
				if (workingSet.Length == 0)
					workingSet = nearestCorrelatedPoints;

				Debug.WriteLine(workingSet.Average(item => item.Distance(testVariable)));
				//Debug.WriteLine(workingSet[0].Correlation(testVariable));

				//вычисление наиболее частых разниц мячей среди ближайших [corrleatedSetCount] триплетов
				Dictionary<int, int> diffFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet)
				{
					if (diffFrequency.ContainsKey(triplet.GDiff))
						diffFrequency[triplet.GDiff] += 1;
					else
						diffFrequency[triplet.GDiff] = 1;
				}
				int mostFrequentGoalsDiff = diffFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

				//вычисление наиболее частого результата
				Dictionary<int, int> hgFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet.Where(item => item.GDiff == mostFrequentGoalsDiff))
				{
					if (hgFrequency.ContainsKey(triplet.HG))
						hgFrequency[triplet.HG] += 1;
					else
						hgFrequency[triplet.HG] = 1;
				}
				int mostFrequentHomeGoals = hgFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

				averallBall += CalculateBall(testVariable.HG, testVariable.HG - testVariable.GDiff,
											 mostFrequentHomeGoals, mostFrequentHomeGoals - mostFrequentGoalsDiff);
			}

			//добиваемся 52%, сейчас - 35.8%
			MessageBox.Show(String.Format("Max result is {0}, taked {1} ({2}%).",
										  testSet.Count * 3,
										  averallBall,
										  ((averallBall / (testSet.Count * 3.0)) * 100).ToString("0.00")));
		}

		private static void Forecast()
		{
			//TODO: загрузить пары дивизиона в формат forecastPairs = List<Ivec>
			var forecastPairs = new List<TeamPoint>();

			ButsaDbContext dbContext = new ButsaDbContext();
			var forecastPairsLearnSet = dbContext.ForecastPairs
				.Where(item => item.ForecastSnapshot.ResultsApplied)
				.Include(p => p.HomeTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.Include(p => p.GuestTeamShot.PlayerShots.Select(ps => ps.BonusShots))
				.ToList();

			//анализируемое множество
			var learnSet = forecastPairsLearnSet.Select(item => new Ivec(item))
				.Select(item => new TeamPoint
				{
					HomeTeamEstimation = item.HomeTeamEstimation,
					GuestTeamEstimation = item.GuestTeamEstimation,
					HG = item.HomeGoals,
					GDiff = item.HomeGoals - item.GuestGoals
				})
				.ToList();


			forecastPairs = forecastPairs
				.OrderByDescending(item => Math.Abs(item.HomeOtherGuests))
				.ToList()
				.Take(16)
				.ToList();
			foreach (var triplet in forecastPairs)
			{
				Debug.WriteLine(triplet.HomeOtherGuests);
			}

			//рассчет набранных балов на тестовом множестве
			const int nearestSetCount = 40;
			const int corrleatedSetCount = 15;
			foreach (var fpVarible in forecastPairs)
			{
				#region поиск [nearestSetCount] ближайших точек из learnSet

				TeamPoint[] nearestPoints = learnSet.Take(nearestSetCount).ToArray();
				for (int i = nearestSetCount; i < learnSet.Count; i++)
				{
					var learnPoint = learnSet[i];

					//среди 20 триплетов находим те, что от testVariable лежат дальше, чем learnPoint
					//и берем самый далекий
					TeamPoint replacedPoint = nearestPoints
						.Where(item => item.Distance(fpVarible) - learnPoint.Distance(fpVarible) > 0)
						.OrderByDescending(item => item.Distance(fpVarible) - learnPoint.Distance(fpVarible))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestPoints, replacedPoint);
						nearestPoints[replaceIndex] = learnPoint;
					}
				}

				#endregion

				#region поиск наиболее коррелирующих значений из набора ближайших [triplets]

				TeamPoint[] nearestCorrelatedPoints = nearestPoints.Take(corrleatedSetCount).ToArray();
				for (int i = corrleatedSetCount; i < nearestPoints.Length; i++)
				{
					var testPoint = nearestPoints[i];

					//среди [nearestSetCount] ближайших точек находим те, что хуже, чем testPoint коррелируют с testVariable
					TeamPoint replacedPoint = nearestCorrelatedPoints
						.Where(item => item.Correlation(fpVarible) < testPoint.Correlation(fpVarible))
						.OrderByDescending(item => item.Correlation(fpVarible) - testPoint.Correlation(fpVarible))
						.FirstOrDefault();

					if (replacedPoint != null)
					{
						int replaceIndex = Array.IndexOf(nearestCorrelatedPoints, replacedPoint);
						nearestCorrelatedPoints[replaceIndex] = testPoint;
					}
				}

				#endregion

				TeamPoint[] workingSet = nearestCorrelatedPoints.Where(item => item.Distance(fpVarible) < 5).ToArray();
				if (workingSet.Length == 0)
					workingSet = nearestCorrelatedPoints;

				Debug.WriteLine(workingSet.Average(item => item.Distance(fpVarible)));
				//Debug.WriteLine(workingSet[0].Correlation(testVariable));

				//вычисление наиболее частых разниц мячей среди ближайших [corrleatedSetCount] триплетов
				Dictionary<int, int> diffFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet)
				{
					if (diffFrequency.ContainsKey(triplet.GDiff))
						diffFrequency[triplet.GDiff] += 1;
					else
						diffFrequency[triplet.GDiff] = 1;
				}
				int mostFrequentGoalsDiff = diffFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

				//вычисление наиболее частого результата
				Dictionary<int, int> hgFrequency = new Dictionary<int, int>();
				foreach (var triplet in workingSet.Where(item => item.GDiff == mostFrequentGoalsDiff))
				{
					if (hgFrequency.ContainsKey(triplet.HG))
						hgFrequency[triplet.HG] += 1;
					else
						hgFrequency[triplet.HG] = 1;
				}
				int mostFrequentHomeGoals = hgFrequency.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;
			}
		}

		private static int CalculateBall(int hg, int gg, int hgf, int ggf)
		{
			int b = 0;
			if (hg == hgf && gg == ggf)
				b = 3;
			else if (hg - gg == hgf - ggf)
				b = 2;
			else if (Math.Sign(hg - gg) == Math.Sign(hgf - ggf))
				b = 1;
			return b;
		}
	
	}
}

